﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace SimpleORM
{
    public class SelectTranslator : LambdaTranslator
    {
        #region Properties

        public Type ObjectType { get; private set; }
        public List<string> ColumnAliasNames { get; private set; }

        public Dictionary<string, IScriptFactory> GroupByKeys { get; set; }
        public Type GroupByType { get; set; }

        #endregion

        #region Ctor

        public SelectTranslator()
        {
            ColumnAliasNames = new List<string>();
            GroupByKeys = new Dictionary<string, IScriptFactory>();
        }

        #endregion

        #region Methods

        #region Methods - Public

        public override void Translate(Expression lambda)
        {
            base.Translate(lambda);
            Stack.Push(Result);

            List<IScriptFactory> items = Stack.ToList();
            items.Reverse();
            for (int i = 0; i < items.Count; i++)
                items[i] = items[i].As(DataSource.CreateScriptFactory().
                                    Constant(ColumnAliasNames[i]).
                                    Stress());

            Result = new SelectObjectContext().BuildSelect(items);
        }


        #endregion

        #region Methods - Visit

        protected override Expression VisitParameter(ParameterExpression p)
        {
            if (!FromNameDictionary.ContainsKey(p.Type))
                return p;

            ObjectType = p.Type;

            SelectObjectContext e = new SelectObjectContext();
            e.Factory = DataSource.CreateScriptFactory();
            e.Install(ObjectContextDictionary[ObjectType],
                      FromNameDictionary[ObjectType]);

            e.SelectObjectItems.ForEach(item => Stack.Push(item.GetItemName()));
            e.SelectObjectItems.ForEach(item => ColumnAliasNames.Add(item.AliasName));

            return base.VisitParameter(p);
        }

        protected override NewExpression VisitNew(NewExpression n)
        {
            ObjectType = n.Type;
            foreach (var item in n.Members)
                ColumnAliasNames.Add(item.Name);

            return base.VisitNew(n);
        }

        protected override Expression VisitMemberInit(MemberInitExpression init)
        {
            ObjectType = init.NewExpression.Type;
            foreach (var item in init.Bindings)
                ColumnAliasNames.Add(item.Member.Name);

            return base.VisitMemberInit(init);
        }

        protected override Expression VisitMethodCall(MethodCallExpression m)
        {
            var first = m.FindFirst();
            if (first.Arguments.Count == 0)
                return base.VisitMethodCall(m);

            Type type = first.Arguments[0].Type;

            //Type type = m.FindFirst().Arguments[0].Type;

            if (type.IsKindOf(typeof(IQueryable)))
            {
                ExpressionTranslater t = DataSource.CreateExpressionTranslater();
                t.Translate(new QueryableExpressionExpander().Expand(m));

                Stack.Push(t.Result.Bracket());

                return m;
            }

            return base.VisitMethodCall(m);
        }

        protected override Expression VisitMemberAccess(MemberExpression m)
        {
            if (FromNameDictionary.ContainsKey(m.Type))
            {
                ObjectType = m.Type;

                SelectObjectContext e = new SelectObjectContext();
                e.Factory = DataSource.CreateScriptFactory();
                e.Install(ObjectContextDictionary[m.Type],
                          FromNameDictionary[m.Type]);

                e.SelectObjectItems.ForEach(item => Stack.Push(item.GetItemName()));
                e.SelectObjectItems.ForEach(item => ColumnAliasNames.Add(item.AliasName));

                return m;
            }

            if (ObjectType == null)
            {
                ObjectType = m.Type;
                ColumnAliasNames.Add(m.Member.Name);
            }

            Type type = null;

            if (m.Expression is MemberExpression)
                type = (m.Expression as MemberExpression).Type;
            else if (m.Expression is ParameterExpression)
                type = (m.Expression as ParameterExpression).Type;

            if (type.FullName.StartsWith("System.Linq.IGrouping`") &&
                m.Member.Name == "Key")
            {
                Stack.Push(GroupByKeys.Values.FirstOrDefault());
                return m;
            }
            else if (type == GroupByType && GroupByKeys.ContainsKey(m.Member.Name))
            {
                Stack.Push(GroupByKeys[m.Member.Name]);
                return m;
            }

            return base.VisitMemberAccess(m);
        }

        //protected override Expression VisitConstant(ConstantExpression c)
        //{
        //    if (c.Value is IQueryable)
        //        return c;

        //    return base.VisitConstant(c);
        //}

        #endregion

        #endregion
    }
}
